package alocador.ui;

import java.util.regex.Pattern;

import alocador.exceptions.DataInvalidaException;
import alocador.exceptions.EventoInvalidoException;
import alocador.exceptions.EventoNaoExistenteException;
import alocador.exceptions.FinalidadeInvalidaException;
import alocador.exceptions.SalaInvalidaException;
import alocador.exceptions.TipoInvalidoException;
import alocador.util.MetodoEntrada;
import alocador.util.SistemaAlocacaoSalasFacade;

/**
 * Camada de interecao com o Usuario e fluxo principal de execucao do sistema
 */
public class InterfaceTextual {

	private SistemaAlocacaoSalasFacade sistema;

	/**
	 * Inicializar o Sistema
	 */
	public void inicializar() {
		sistema.carregaSistema();
		System.out.println(Texto.promptSaudacao());
		mostrarMenuPrincipal();
	}

	/**
	 * Cria uma nova instancia de InterfaceTextual
	 */
	public InterfaceTextual() {
		this.sistema = new SistemaAlocacaoSalasFacade();
	}

	/**
	 * Funcionalidades do Sistema
	 */
	private void mostrarMenuPrincipal() {

		boolean repetirMenu = true;
		MENU_PRINCIPAL:

		while (repetirMenu) {

			System.out.print(Texto.menuPrincipal());
			OpcaoMenu opcao = OpcaoMenu.escolher(MetodoEntrada.recebeInt());

			switch (opcao) {

			case ADICIONAR_SALA:
				adicionarSala();
				break;

			case ADICIONAR_EVENTO:
				adicionarEvento();
				break;

			case ALOCAR_EVENTO:
				alocarEvento();
				break;

			case REMOVER_SALA:
				removerSala();
				break;

			case CANCELAR_EVENTO:
				cancelarEvento();
				break;

			case DESALOCAR_EVENTO:
				desalocarEvento();
				break;

			case LOCALIZAR_EVENTO:
				localizarEvento();
				break;
				
			case ZERAR_SISTEMA:
				if (confirmarRequisicao(Texto.zerarSistema()))
					System.out.println(Texto.arquivosDeletados());
					sistema.zerarSistema();
				break;

			case SAIR_DO_SISTEMA:
				if (confirmarRequisicao(Texto.opcaoSairDoSistema())) {
					sistema.encerrarSistema();
					break MENU_PRINCIPAL;

				} else
					break;

			default:
				System.out.println(Texto.promptOpcaoInvalida());
				break;
			}
		}
	}

	/**
	 * Delega atividade ao responsavel por adicionar sala
	 */
	private void adicionarSala() {
		System.out.println(Texto.opcaoAdicionarSala());

		System.out.print(Texto.promptID());
		String id = MetodoEntrada.recebeString();

		System.out.print(Texto.promptCapacidade());
		int capacidade = MetodoEntrada.recebeInt();

		System.out.print(Texto.promptFinalidade());
		String finalidade = MetodoEntrada.recebeFinalidade();

		boolean aberta = false;
		if (finalidade.equalsIgnoreCase("laboratorio")) {
			aberta = confirmarRequisicao(Texto.perguntaSeEstaAberto());
		}

		boolean temApelido = confirmarRequisicao(Texto.perguntaApelido());
		String apelido = "";
		if (temApelido) {
			System.out.print(Texto.prompApelido());
			apelido = MetodoEntrada.recebeString();

		}
		String tipo = identficaTipoSala(finalidade);

		try {

			// Qualquer tipo de sala, caso ela nao tenha apelido
			if (!temApelido)
				sistema.adicionarSala(id, capacidade, finalidade, tipo);

			// Laboratorios com apelido
			else if (finalidade.equalsIgnoreCase("laboratorio")) {
				sistema.adicionarSala(id, capacidade, finalidade, tipo,
						apelido, aberta);
			}
			// Qualquer tipo de sala, caso ela tenha apelido
			else {
				sistema.adicionarSala(id, capacidade, finalidade, tipo, apelido);
			}
			System.out.println(Texto.promptOperacaoSucesso());

		} catch (FinalidadeInvalidaException e) {
			System.out.println(">>> ERRO: " + e.getMessage());

		} catch (TipoInvalidoException e) {
			System.out.println(">>> ERRO: " + e.getMessage());

		} catch (SalaInvalidaException e) {
			System.out.println(">>> ERRO: " + e.getMessage());

		} catch (Exception e) {
			//FIXME: LANCAR EXCESSAO CORRETA
			System.out.println(">>> ERRO: " + e.getMessage());
		}
	}

	/**
	 * Delega atividade ao responsavel por adicionar evento
	 */
	private void adicionarEvento() {
		System.out.println(Texto.opcaoAdicionarEvento());

		System.out.print(Texto.promptID());
		String id = MetodoEntrada.recebeString();

		System.out.print(Texto.promptNome());
		String nome = MetodoEntrada.recebeString();

		System.out.println(Texto.helpData());
		System.out.print(Texto.promptData() + "(Inicio) ");
		String inicio = MetodoEntrada.recebeDataHora();

		System.out.print(Texto.promptData() + "(Fim) ");
		String fim = MetodoEntrada.recebeDataHora();

		System.out.print(Texto.promptArea());
		String area = MetodoEntrada.recebeString();

		System.out.print(Texto.promptContato());
		String contato = MetodoEntrada.recebeString();

		boolean temRepeticoes = confirmarRequisicao(Texto.perguntaRepeticoes());

		try {

			if (temRepeticoes) {
				System.out.print(Texto.promptRepeticoes());
				int repeticoes = MetodoEntrada.recebeInt();
				sistema.adicionarEvento(id, nome, inicio, fim, area, contato,
						repeticoes);
			} else
				sistema.adicionarEvento(id, nome, inicio, fim, area, contato);

			System.out.println(Texto.promptOperacaoSucesso());
		} catch (EventoInvalidoException e) {
			System.out.println(">>> ERRO: " + e.getMessage());

		} catch (DataInvalidaException e) {
			System.out.println(">>> ERRO: " + e.getMessage());

		} catch (EventoNaoExistenteException e) {
			System.out.println(">>> ERRO: " + e.getMessage());
		}

	}

	/**
	 * Delega atividade ao responsavel por alocar evento
	 */
	private void alocarEvento() {
		System.out.println(Texto.opcaoAlocarEvento());

		System.out.print(Texto.promptID() + "(Evento) ");
		String idEvento = MetodoEntrada.recebeString();

		System.out.print(Texto.promptID() + "(Sala) ");
		String idSala = MetodoEntrada.recebeString();
		try {
			sistema.alocarEvento(idEvento, idSala);

			System.out.println(">>> EVENTO: " + idEvento + "SALA: " + idSala
					+ " alocados.");
			System.out.println(Texto.promptOperacaoSucesso());

		} catch (Exception e) {
			System.out.println(">>> ERRO: " + e.getMessage());
		}

	}

	/**
	 * Delega atividade ao responsavel por localizar evento
	 */
	private void localizarEvento() {
		System.out.println(Texto.opcaoLocalizarEvento());

		System.out.print(Texto.promptAtributo());
		String atributo = MetodoEntrada.recebeString().toLowerCase();

		System.out.print(Texto.promptValor());
		String valor = MetodoEntrada.recebeString();

		String resultado = "";
		try {

			resultado = sistema.localizarEvento(atributo, valor);
			System.out.println(">>> EVENTO: " + resultado);

		} catch (Exception e) {
			// FIXME Definir EXCEPTION
			System.out.println(">>> ERRO: " + e.getMessage());
		}

	}

	/**
	 * Delega atividade ao responsavel por desalocar evento
	 */
	private void desalocarEvento() {
		System.out.print(Texto.opcaoDesalocarEvento());
		String idEvento = MetodoEntrada.recebeString();
		try {
			sistema.desalocarEvento(idEvento);
			System.out.println(Texto.promptOperacaoSucesso());

		} catch (EventoNaoExistenteException e) {
			System.out.println(">>> ERRO: " + e.getMessage());

		} catch (EventoInvalidoException e) {
			System.out.println(">>> ERRO: " + e.getMessage());
		}

	}

	/**
	 * Delega atividade ao responsavel por cancelar evento
	 */
	private void cancelarEvento() {
		System.out.print(Texto.opcaoCancelarEvento());
		String idEvento = MetodoEntrada.recebeString();
		try {
			sistema.cancelarEvento(idEvento);
			System.out.println(Texto.promptOperacaoSucesso());

		} catch (EventoNaoExistenteException e) {
			System.out.println(">>> ERRO: " + e.getMessage());

		} catch (EventoInvalidoException e) {
			System.out.println(">>> ERRO: " + e.getMessage());
		}
	}

	/**
	 * Delega atividade ao responsavel por remover sala
	 */
	private void removerSala() {
		System.out.print(Texto.opcaoRemoverSala());
		String idSala = MetodoEntrada.recebeString();
		try {
			// FIXME REMOVER SALA DEVERIA RETORNAR A SALA REMOVIDA
			sistema.removerSala(idSala);
			System.out.println(Texto.promptOperacaoSucesso());

		} catch (SalaInvalidaException e) {
			System.out.println(">>> ERRO: " + e.getMessage());
		}

	}

	/**
	 * Verifica opcao do usuario para um determinada Requisicao do sistema.<br>
	 * O metodo tem um comportamento recursivo, caso a cadeia informada pelo <br>
	 * usuario nao seja valida.
	 * 
	 * @see <i>http://www.regular-expressions.info/reference.html</i>
	 * 
	 * @param requisicao
	 *            Prompt de requisicao a ser feita
	 * @return <code>true</code> se a cadeia for reconhecida pela expressao
	 *         <code>(S|s)(I|i)*(M|m)*</code><br>
	 *         <code>false</code> se a cadeia for reconhecida pela expressao
	 *         <code>(N|n)(A|a)*(O|o)*</code><br>
	 * 
	 */
	private boolean confirmarRequisicao(String requisicao) {
		System.out.print(requisicao);
		String opcao = MetodoEntrada.recebeString();

		if (Pattern.matches("(S|s)(I|i)*(M|m)*", opcao)) {
			return true;

		} else if (Pattern.matches("(N|n)(A|a)*(O|o)*", opcao)) {
			return false;

		} else {
			System.out.println(Texto.promptOpcaoInvalida());
			return confirmarRequisicao(requisicao);
		}

	}

	/**
	 * Redireciona os tipos de salas especificos de acordo com a finalidade da
	 * sala que sera criada. <br>
	 * 
	 * <b>Importante:</b> Caso a finalidade seja idenficada pelo metodo, sera
	 * retornado uma string vazia
	 * 
	 * @see {@link MetodoEntrada#tiposSalaConferencia()}
	 * @see {@link MetodoEntrada#tiposLaboratorio()}
	 * @see {@link MetodoEntrada#tiposSalaAula()}
	 * 
	 * @param finalidade
	 *            A finalidade da sala a ser instanciada
	 * @return O tipo da sala a ser instanciada
	 */
	private static String identficaTipoSala(String finalidade) {
		if (finalidade != null) {
			if (finalidade.equalsIgnoreCase("laboratorio")) {
				System.out.print(Texto.promptTiposLab());
				return MetodoEntrada.tiposLaboratorio();
			}
			if (finalidade.equalsIgnoreCase("sala de conferencia")) {
				System.out.print(Texto.promptTiposSalaConf());
				return MetodoEntrada.tiposSalaConferencia();
			}
			if (finalidade.equalsIgnoreCase("sala de aula")) {
				System.out.print(Texto.promptTiposSala());
				return MetodoEntrada.tiposSalaAula();
			}
			if (finalidade.equalsIgnoreCase("escritorio"))
				return finalidade;
		}
		return "";
	}

}